package com.yx.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yx.constatns.OrderConstants;
import com.yx.entity.Order;
import com.yx.entity.OrderDetail;
import com.yx.exception.StockMsg;
import com.yx.exception.vo.StockException;
import com.yx.feign.api.AddressService;
import com.yx.feign.api.CarService;
import com.yx.feign.api.StockService;
import com.yx.interceptor.DynamicDataSourceAnnotationInterceptor;
import com.yx.mapper.OrderMapper;
import com.yx.service.IOrderDetailService;
import com.yx.service.IOrderService;
import com.yx.thread.ExecutorUtils;
import com.yx.utils.OrderUtils;
import com.yx.vo.Address;
import com.yx.vo.Car;
import com.yx.vo.OrderParam;
import com.yx.vo.StockParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @Classname OrderServiceImpl
 * @Date 2021/07/20 19:08
 * @Author by yx
 * @Description TODO:
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private CarService carService;
    @Autowired
    private AddressService addressService;
    @Autowired
    private IOrderDetailService orderDetailService;
    @Autowired
    private StockService stockService;

    @Autowired
    private DynamicDataSourceAnnotationInterceptor dataSourceInterceptor;

    @Autowired
    private OrderUtils orderUtils;

    @Override
    @Transactional
    public String createOrder(OrderParam orderParam, Integer uid) throws ExecutionException, InterruptedException {
        // 获取自定义的线程池
        ThreadPoolExecutor pool = ExecutorUtils.getExcutorsPool();
        // 获取主线程中的tableIndex;利用拦截器获取
        Integer tableIndex = dataSourceInterceptor.getTableIndex();
        String dbIndex = DynamicDataSourceContextHolder.peek();

        // 1.查询用户地址任务
        CompletableFuture<Address> addressFuture = CompletableFuture.supplyAsync(() -> {
            //获取地址信息
            return addressService.getAddressByid(orderParam.getAddressId());
        }, pool);

        // 2. 查询用户购物车任务
        CompletableFuture<Map<String, Object>> carFuture = CompletableFuture.supplyAsync(() -> {
            Map<String, Object> carMap = null;
            //1.判断用户是否勾选支付
            if (!StringUtils.isEmpty(orderParam.getGid())) {
                //勾选支付
                //切分gid
                List<String> gidList = Arrays.asList(orderParam.getGid().split(","));
                //获取购物车
                carMap = carService.getCarListByUidAndGid(uid, gidList);
            } else {
                // 没有勾选
                carMap = carService.getCarListByUid(uid);
            }

            //获取购物车总价
            BigDecimal totalPrice = BigDecimal.valueOf(Long.valueOf(carMap.get("totalPrice").toString()));
            //获取购物车商品
            List<Map<String, Object>> list = (List<Map<String, Object>>) carMap.get("carList");
            List<Car> carList = JSON.parseArray(JSON.toJSONString(list), Car.class); // 先把list转成JSON字符串

            // 为方便下面线程使用,再次封装数据
            Map<String, Object> carObjectMap = new HashMap<>();
            carObjectMap.put("totalPrice", totalPrice);
            carObjectMap.put("carList", carList);

            return carObjectMap;
        }, pool);

        /*
         * 组合地址和购物车任务--->完成后在进行下面的任务
         * 扣减库存操作需要在获取购物车数据之后才能进行
         * 生成订单,需要成功扣减库存和获取地址数据之后才能进行
         * 生成订单详情需要在订单号,必须在生成订单之后进行
         * */
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(addressFuture, carFuture);
        // 阻塞任务,直到两个任务都完成
        Void aVoid = voidCompletableFuture.get();

        // 3. 扣减库存任务
        CompletableFuture<String> orderFuture = CompletableFuture.supplyAsync(() -> {
            try {
                // 从购物车任务中获取返回的购物车数据
                Map<String, Object> carMap = carFuture.get();
                List<Car> carList = (List<Car>) carMap.get("carList");

                // 计算库存
                List<StockParam> stockParamList = new ArrayList<>();
                for (Car car : carList) {
                    StockParam stockParam = new StockParam();
                    stockParam.setGid(car.getGid());
                    stockParam.setSum(car.getSum());
                    // 还未实现根据地址,自动选择最近仓库,暂时设置仓库为1
                    stockParam.setWid(1);
                    stockParamList.add(stockParam);
                }

                // 返回扣减库存状态 --->
                return stockService.lockGoodsStock(stockParamList);

            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

            return null;
        }, pool)
                .thenApplyAsync((stockFlag) -> {
                    /*
                    * 异步编程原因,导致不是同一线程,拿不到数据库编号,走了默认的数据库编号,导致支付时查询失败
                    * 手动加载动态数据源数据库编号
                    * */
                    DynamicDataSourceContextHolder.push(dbIndex);

                    // 接收前一个任务的执行结果，并消费，该任务有返回结果
                    if (stockFlag) {
                        //根据uid自动生成oid
                        String oid = orderUtils.createOrderId(uid);
                        try {
                            Address address = addressFuture.get();
                            Map<String, Object> map = carFuture.get();
                            List<Car> carList = (List<Car>) map.get("carList");
                            BigDecimal totalPrice = (BigDecimal) map.get("totalPrice");

                            // 3.创建订单
                            Order order = new Order();
                            order.setOid(oid);
                            order.setUid(uid);
                            order.setAddress(address.getAddress());
                            order.setName(address.getName());
                            order.setPhone(address.getPhone());
                            order.setCreateTime(new Date());
                            order.setStatus(1);
                            order.setTotalPrice(totalPrice);
                            order.setPayType(2);

                            // 动态数据源 存入数据库
                            // 将订单信息存入数据库
                            baseMapper.insertOrder(order, tableIndex);

                            //创建订单详情表
                            List<OrderDetail> orderDetailList = new ArrayList<>();

                            //一个订单可能有多个商品购物车(每一个商品)
                            for (Car car : carList) {
                                OrderDetail orderDetail = new OrderDetail();
                                StockParam stockParam = new StockParam();
                                orderDetail.setGid(car.getGid());
                                orderDetail.setGdesc(car.getGoods().getGdesc()); // 历史数据
                                orderDetail.setGname(car.getGoods().getGname());
                                orderDetail.setGpng(car.getGoods().getGpng());
                                orderDetail.setGprice(car.getGoods().getGprice());
                                orderDetail.setOid(oid);
                                orderDetail.setSum(car.getSum());
                                orderDetail.setSubTotal(car.getSubTotal());

                                //添加至集合
                                orderDetailList.add(orderDetail);
                            }

                            // 批量添加到数据库
                            orderDetailService.saveBatchOrderDetail(orderDetailList, tableIndex);

                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (ExecutionException e) {
                            e.printStackTrace();
                        }
                        return oid;
                    } else {
                        log.debug("{}", "扣减库存失败");
                        return null;
                    }
                }, pool);

        String oid = orderFuture.get();
        System.out.println("最后的oid:" + oid);
        if(StringUtils.isEmpty(oid)){
            throw new StockException(StockMsg.UNLOCK_STOCK_ERROR);
        }
        return oid;
    }

    @Override
    public Order getOrderByOid(String oid) {
        // 从订单号中截取uid
        Integer uid = Integer.valueOf(oid.substring(13,17));
        // 计算数据库编号(用户id后四位%数据库的数量)
        Integer dbIndex = (uid % OrderConstants.ORDER_DB_SIZE) + 1;

        // 创建数据源key
        String dataSourceKey = OrderConstants.ORDER_DB_PRE + dbIndex;

        // 设置数据源
        DynamicDataSourceContextHolder.push(dataSourceKey);

        // 计算订单的表的编号(在插入的时候才会使用这个值，我在这个地方一起计算出来)
        Integer tableIndex = (uid / OrderConstants.ORDER_DB_SIZE % OrderConstants.ORDER_TABLE_SIZE) + 1;

        return baseMapper.getOrderByOid(oid,tableIndex);
    }

    @Override
    public void updateOrderStatus(String oid, Integer status) {
        // 从订单号中截取uid
        Integer uid = Integer.valueOf(oid.substring(13,17));
        // 计算数据库编号(用户id后四位%数据库的数量)
        Integer dbIndex = (uid % OrderConstants.ORDER_DB_SIZE) + 1;

        // 创建数据源key
        String dataSourceKey = OrderConstants.ORDER_DB_PRE + dbIndex;

        // 设置数据源
        DynamicDataSourceContextHolder.push(dataSourceKey);

        // 计算订单的表的编号(在插入的时候才会使用这个值，我在这个地方一起计算出来)
        Integer tableIndex = (uid / OrderConstants.ORDER_DB_SIZE % OrderConstants.ORDER_TABLE_SIZE) + 1;

        baseMapper.updateOrderStatus(oid,status,tableIndex);
    }



    /*@Override
    @Transactional //开启事务
    public String createOrder(OrderParam orderParam, Integer uid) {

        Map<String, Object> carMap = null;

        //1.判断用户是否勾选支付
        if (!StringUtils.isEmpty(orderParam.getGid())) {
            //勾选支付
            //切分gid
            List<String> gidList = Arrays.asList(orderParam.getGid().split(","));
            //获取购物车
            carMap = carService.getCarListByUidAndGid(uid, gidList);
        } else {
            // 没有勾选
            carMap = carService.getCarListByUid(uid);
        }

        //获取购物车总价
        BigDecimal totalPrice = BigDecimal.valueOf(Long.valueOf(carMap.get("totalPrice").toString()));
        //获取购物车商品
        List<Map<String, Object>> list = (List<Map<String, Object>>) carMap.get("carList");
        List<Car> carList = JSON.parseArray(JSON.toJSONString(list), Car.class); // 先把list转成JSON字符串

        // 计算库存
        List<StockParam> stockParamList = new ArrayList<>();
        for (Car car : carList) {
            StockParam stockParam = new StockParam();
            stockParam.setGid(car.getGid());
            stockParam.setSum(car.getSum());
            // 还未实现根据地址,自动选择最近仓库,暂时设置仓库为1
            stockParam.setWid(1);
            stockParamList.add(stockParam);
        }

        // 扣减库存
        Boolean stockLock = stockService.lockGoodsStock(stockParamList);
        if (!stockLock) {
            // 库存不足 抛出异常
            throw new StockException(StockMsg.UNLOCK_STOCK_ERROR);
        }

        //获取地址信息
        Address address = addressService.getAddressByid(orderParam.getAddressId());

        //根据uid自动生成oid
        String oid = orderUtils.createOrderId(uid);

        // 3.创建订单
        Order order = new Order();
        order.setOid(oid);
        order.setUid(uid);
        order.setAddress(address.getAddress());
        order.setName(address.getName());
        order.setPhone(address.getPhone());
        order.setCreateTime(new Date());
        order.setStatus(1);
        order.setTotalPrice(totalPrice);
        order.setPayType(2);

        // 动态数据源 存入数据库
        // 从当前线程threadLocal获取tableIndex
        Integer tableIndex = dataSourceInterceptor.getTableIndex();

        log.debug("tableIndex:{}", tableIndex);

        //将订单信息存入数据库
//        baseMapper.insert(order);
        baseMapper.insertOrder(order, tableIndex);

        //创建订单详情表
        List<OrderDetail> orderDetailList = new ArrayList<>();


        //一个订单可能有多个商品购物车(每一个商品)
        for (Car car : carList) {
            OrderDetail orderDetail = new OrderDetail();
            StockParam stockParam = new StockParam();
            orderDetail.setGid(car.getGid());
            orderDetail.setGdesc(car.getGoods().getGdesc()); // 历史数据
            orderDetail.setGname(car.getGoods().getGname());
            orderDetail.setGpng(car.getGoods().getGpng());
            orderDetail.setGprice(car.getGoods().getGprice());
            orderDetail.setOid(oid);
            orderDetail.setSum(car.getSum());
            orderDetail.setSubTotal(car.getSubTotal());

            //添加至集合
            orderDetailList.add(orderDetail);
        }

        // 批量添加到数据库
//        orderDetailService.saveBatch(orderDetailList);
        orderDetailService.saveBatchOrderDetail(orderDetailList, tableIndex);

        return oid;
    }*/
}
